package net.aiion.weave;

import net.aiion.weave.spi.interception.Interceptable;
import net.aiion.weave.spi.interception.InterceptionException;
import net.aiion.weave.spi.interception.InterceptionService;

public class InterceptableClass implements Interceptable
{
    private InterceptionService interceptionService;

    public InterceptableClass() {
        System.out.println("Hello from super!");
    }

    @Override
    public void setInterceptionService(InterceptionService aInterceptionService) {
        interceptionService = aInterceptionService;
    }

    // public int interceptedMethod(int a) {
    // return a + 2;
    // }

    public int interceptedMethod(int a) {
        try {
            return (int) interceptionService.interceptInvocation(this,
                InterceptableClass.class.getMethod("interceptedMethod$$Interception0", int.class), new Object[] {
                    a
                });
        } catch (NoSuchMethodException | SecurityException e) {
            throw new InterceptionException(e);
        }

        // mv = cw.visitMethod(ACC_PUBLIC, "interceptedMethod", "(I)I", null, null);
        // mv.visitCode();
        // Label l0 = new Label();
        // Label l1 = new Label();
        // Label l2 = new Label();
        // mv.visitTryCatchBlock(l0, l1, l2, "java/lang/NoSuchMethodException");
        // mv.visitTryCatchBlock(l0, l1, l2, "java/lang/SecurityException");
        //
        // mv.visitLabel(l0);
        // mv.visitVarInsn(ALOAD, 0);
        // mv.visitFieldInsn(GETFIELD, "net/aiion/weave/InterceptableClass", "interceptionService",
        // "Lnet/aiion/weave/interception/spi/InterceptionService;");
        //
        // // arg0
        // mv.visitVarInsn(ALOAD, 0);
        //
        // // arg1
        // mv.visitLdcInsn(Type.getType("Lnet/aiion/weave/InterceptableClass;"));
        // mv.visitLdcInsn("interceptedMethod$$Interception0");
        // mv.visitInsn(ICONST_1);
        // mv.visitTypeInsn(ANEWARRAY, "java/lang/Class");
        // mv.visitInsn(DUP);
        // mv.visitInsn(ICONST_0);
        // mv.visitFieldInsn(GETSTATIC, "java/lang/Integer", "TYPE", "Ljava/lang/Class;");
        // mv.visitInsn(AASTORE);
        // mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getMethod",
        // "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;");
        //
        // // arg2
        // mv.visitInsn(ICONST_1);
        // mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
        // mv.visitInsn(DUP);
        // mv.visitInsn(ICONST_0);
        // mv.visitVarInsn(ILOAD, 1);
        // mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;");
        // mv.visitInsn(AASTORE);
        //
        // mv.visitMethodInsn(INVOKEINTERFACE, "net/aiion/weave/interception/spi/InterceptionService",
        // "interceptInvocation",
        // "(Lnet/aiion/weave/interception/spi/Interceptable;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;");
        //
        // mv.visitTypeInsn(CHECKCAST, "java/lang/Integer");
        // mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I");
        // mv.visitLabel(l1);
        // mv.visitInsn(IRETURN);
        // mv.visitLabel(l2);
        // mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {
        // "java/lang/Exception"
        // });
        // mv.visitVarInsn(ASTORE, 2);
        // mv.visitTypeInsn(NEW, "net/aiion/weave/interception/spi/InterceptionException");
        // mv.visitInsn(DUP);
        // mv.visitVarInsn(ALOAD, 2);
        // mv.visitMethodInsn(INVOKESPECIAL, "net/aiion/weave/interception/spi/InterceptionException", "<init>",
        // "(Ljava/lang/Throwable;)V");
        // mv.visitInsn(ATHROW);
        // mv.visitMaxs(8, 3);
        // mv.visitEnd();

        /*
         * Bedingung für Komponenten: Sie müssen einen parameterlosen Konstruktor definieren.
         * InjectionService wird per Setter (InjectableComponent.setInjectionService(InjectionService)) an die
         * Komponente gesetzt. Injection kann dann durchgeführt werden. Ähnliches gilt für InterceptionService
         * mittels InterceptableComponent.setInterceptionService(InterceptionService).
         * 
         * Interception und auch Injection als 2 voll voneinander getrennte Dienste betrachten. Mithilfe von
         * ComponentContext kann bei Initialisierung der Komponente entsprechende Dienste erfragt werden und ggf. in
         * privaten Member-Variablen hinterlegt werden (InjectionService, InterceptionService). An entsprechenden
         * Stellen können diese dann verwendet werden.
         * 
         * Vorerst nur BeforeInvocation und AfterInvocation Interception
         * anbieten. Around wird wohl schwieriger (siehe oben).
         */
    }

    public int interceptedMethod$$Interception0(int a) {
        return a + 2;
    }
}
